The present disclosure generally relates to computing devices, and more particularly to communicating state information between process threads.
An application may include data that is shared between a pool of threads. A given program may have tens, hundreds, or thousands of threads, some of which may operate simultaneously as appropriate. A process may have its own private virtual address space and run one or more threads within that virtual address space. As noted above, some of the threads in a process may share data.
Before accessing shared data, a thread may lock the shared data so that another thread cannot modify the shared data while it is being accessed, to ensure coherency of the data being accessed. It may be desirable, however, to avoid the use of locks for a variety of reasons. To ensure that data accesses are thread safe, a variable may be locked before it is updated. In such a scenario, the lock may be abused by programmers and a higher risk of multiple threads and more complex interactions between the multiple threads may occur, resulting in possible performance loss. The use of locks may provide for complex interactions between threads as well as overhead. It is known that multi-threaded programming and lock management is a major complexity requiring skilled programmers to implement properly, and efficiently. As code ages and goes through maintenance from different engineers, who have varying degrees of expertise on a code block, and who are required to minimize lines of codes (LOC) modified, the use of the locks may become too conservative, trading code performance for safety and lines of code changed.
Additionally, a single concurrency program may manifest itself in many different ways such as deadlocks, live locks, or plain crashes. A deadlock occurs when two threads are sleeping, both waiting for the other to finish using a resource that each of them needs, and the two threads never wake up. A live lock is similar, but involves active processing from the threads. Moreover, performance issues commonly arise from using synchronization. A lock is a bottleneck, as it introduces a critical section that can only be accessed by one thread at a time. The more threads trying to get at a critical section, the more contention will arise as threads have to wait for their turn. Additionally, critical sections may grow and prevent scaling the code implementations. If a lock is badly placed or covers too wide a section in the interest of easier debugging (or just because of general laziness) performance penalties may occur. In complex systems, with many levels of locks, a lock ordering mechanism can be implemented, and its use may prevent deadlocks but may also increase the complexity of implementing access to data.
The purposes of locking data are to enforce coherency and preserve order in conflicting operations. Locking data, however, generates locking overhead. For example, to lock and unlock shared data, a few hundred instructions may need to be executed each time. To eliminate or minimize the disadvantages of locking data, nonlocking-based schemes exist that enforce coherency and preserve order in conflicting operations. For example, a traditional nonlocking technique uses a timestamp approach for serializing the execution of concurrent transactions. In time stamping, the execution order of concurrent transactions is defined before they begin their execution. The execution order is established by associating a unique timestamp (e.g., an integer) to every transaction. When two transactions conflict over data, their timestamps are used to enforce serialization by rolling back one of the conflicting transactions.
Avoiding the use of data locks between threads may eliminate the risk of deadlocks and synchronization delays. Moreover, a lockless approach may also prevent lazy programming that relies on coarse locks to enforce coherency, which may eventually create a hidden scalability issue. Thus, specific approaches avoiding locks are desirable for many applications.
Embodiments of the present disclosure and their advantages are best understood by referring to the detailed description that follows. It should be appreciated that like reference numerals are used to identify like elements illustrated in one or more of the figures. The drawing in which an element first appears is generally indicated by the left-most digit in the corresponding reference number.